<?php  if ( ! defined('BASEPATH')) exit('No direct script access allowed');
/**************************************************************************
 *
 *   Copyright 2010 American Public Media Group
 *
 *   This file is part of AIR2.
 *
 *   AIR2 is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   AIR2 is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with AIR2.  If not, see <http://www.gnu.org/licenses/>.
 *
 *************************************************************************/

/**
 * AIR2 Application Controller Base Class (overrides default CI controller)
 *
 * This class object is the parent class to every controller used in AIR2. It
 * implements security, as well as encapsulating any variables passed in
 * with the client request.
 *
 * @author rcavis
 * @package default
 */
class AIR2_Controller extends Controller {
    public $is_production = false; // production env flag
    public $request_method;
    public $response_view;
    protected $query_args = array();
    protected $paging_args = array();
    public $image_proxies = array(); //optional
    public $valid_args = array(); // optional


    /**
     * Constructor
     *
     * Connect to db, setup request params, init security.
     */
    public function __construct() {
        parent::Controller();

        // set the is_production flag based on setting in app/init.php
        if (AIR2_ENVIRONMENT == 'prod') $this->is_production = true;

        /* always open db connection per request */
        AIR2_DBManager::init();

        // load required libraries
        $this->load->library('AirUser');
        $this->load->library('AirOutput');
        $this->load->library('AirQuery');
        $this->load->library('AirBatch');
        $this->load->library('AirQargs', array('valid_args' => $this->valid_args));

        // set some SERVER vars
        $this->request_method = $this->input->server('REQUEST_METHOD');
        $this->response_view = $this->airoutput->get_output_view();
        $this->_init_query_strings();
        $this->_init_paging_params();

        // allow subclasses to implement their own start-up hook
        $this->begin();
    }


    /**
     * begin() checks for security (authentication) using AirUser library.
     * If you override begin(), be sure to call parent::begin() in your subclass.
     *
     * @method begin()
     */
    public function begin() {
        $this->_init_security();
    }


    /**
     * initialize security for this controller
     *
     * This function determines what (if any) security the user has and
     * encapsulates it in the $AIR2User class variable.  Will return a
     * login page and exit if the user cannot be authenticated.
     *
     * @access private
     * @return void
     */
    private function _init_security() {

        // check if the credentials were good
        if ( !$this->airuser->has_valid_tkt() || !$this->airuser->get_user() ) {
            // user not authenticated
            $this_uri = current_url() . '?' . $this->input->server('QUERY_STRING');
            Carper::carp("Permission denied for $this_uri");
            $uri = $this->uri_for('login', array('back' => $this_uri));
            //echo "Location: $uri\n";
            redirect($uri);
            exit(0);
        }

        //set global remote user ID and TYPE (for upd/cre user stamps)
        define('AIR2_REMOTE_USER_ID', $this->airuser->get_id());
        $tmp = $this->airuser->get_user_info();
        define('AIR2_REMOTE_USER_TYPE', $tmp['type']);

        // authn ok
        return true;

    }


    /**
     * Loads any search parameters passed in through GET
     *
     * @access private
     * @return void
     */
    private function _init_query_strings() {
        $arrkeys = array_keys($_GET);
        foreach ($arrkeys as $key) {
            // perform xss filtering on values
            $this->query_args[$key] = $this->input->get($key, true);
        }
    }


    /**
     * Loads any paging parameters (performing xss filtering)
     *
     * @access private
     * @return void
     */
    private function _init_paging_params() {
        $this->paging_args['dir'] = $this->input->get('dir', true);
        $this->paging_args['sort'] = $this->input->get('sort', true);
        $this->paging_args['start'] = $this->input->get('start', true);
        $this->paging_args['limit'] = $this->input->get('limit', true);
    }


    /**
     * Write out a response
     *
     * @return void
     * @param array   $data (optional) The data to publish
     */
    protected function response($data=array()) {
        if (empty($data)) {
            show_404();
        }
        else {
            $this->airoutput->write($data);
        }
    }


    /**
     * uri_for() will return the full URI for a part of the application.
     * Example:
     *   $this->uri_for('foo/bar', array('color'=>'red'));
     *   //  https://myhost/foo/bar?color=red
     *
     * @param string  $path
     * @param array   $query (optional)
     * @return unknown
     */
    public function uri_for($path, $query=array()) {
        $base = base_url();
        $base = preg_replace('/\/$/', '', $base);
        $path = preg_replace('/^\//', '', $path);
        if (strlen($path)) {
            $uri = $base . '/' . $path;
        }
        else {
            $uri = $base;
        }

        if (count($query)) {
            $uri .= '?' . http_build_query($query);
        }

        return $uri;
    }


    /**
     * Creates basic metadata (must provide 'fields' and 'idProperty').
     * Passing "true" as sort will use any GET parameters instead of explicitly
     * passed ones.
     *
     * @param string  $id_property
     * @param array   $fields_def  an array of associative arrays defining fields
     * @param boolean|array $sort        paging data (sort, dir, start, limit)
     * @return associative array of metadata
     */
    public function _create_metadata_basic($id_property, $fields_def, $sort=null) {
        // create metadata
        $metadata = array();
        $metadata['idProperty'] = $id_property;
        $metadata['root'] = 'radix';
        $metadata['totalProperty'] = 'total';
        $metadata['successProperty'] = 'success';
        $metadata['messageProperty'] = 'message';
        $metadata['fields'] = $fields_def;

        // paging and sort info
        if (is_array($sort)) {
            $metadata['sortInfo'] = array(
                'field' => $sort['sort'],
                'direction' => $sort['dir'] );
            $metadata['start'] = $sort['start'];
            $metadata['limit'] = $sort['limit'];
        }
        elseif ($sort) {
            if ($this->paging_args['sort'] && $this->paging_args['dir']) {
                $metadata['sortInfo'] = array(
                    'field' => $this->paging_args['sort'],
                    'direction' => $this->paging_args['dir']
                );
            }
            if ($this->paging_args['start']) {
                $metadata['start'] = $this->paging_args['start'];
            }
            if ($this->paging_args['limit']) {
                $metadata['limit'] = $this->paging_args['limit'];
            }
        }

        return $metadata;
    }


    /**
     * Custom handling for showing 404 error
     */
    public function show_404() {
        show_404();
    }



    /**
     * Custom handling for showing 403 error
     */
    public function show_403() {
        show_error("Error: permission denied", 403);
    }


    /**
     * Custom handling for showing 415 error
     */
    public function show_415() {
        show_error('', 415);
    }


    /**
     * Request Bin data inline as we're returning HTML, so the Bins display
     * without an ajax call.  The "bin-state" cookie must request that this
     * data be sent.  The cookie has the following json-encoded structure:
     * AIR2_BIN_STATE_CK:
     *   view: (which view is displayed ... 'sm', 'lg' or 'si')
     *   params: (the GET params to load with)
     *   uuid: (the uuid of the batch --- only used in view = 'si')
     *   open: (true or false, whether the drawer is open)
     *
     * @return string 'false' or the json-encoded bin/contents list
     */
    public function get_bin_data() {
        $ck = isset($_COOKIE[AIR2_BIN_STATE_CK]) ? $_COOKIE[AIR2_BIN_STATE_CK] : null;
        if (!$ck) return 'false';
        $json = json_decode($ck, true);

        // get the remote user
        $this_user = $this->airuser->get_user();

        // sanity check!
        if (!$json || !isset($json['params']) || !is_array($json['params'])
            || !isset($json['view'])) {
            return 'false';
        }
        if ($json['view'] != 'si' && isset($json['uuid'])) {
            return 'false';
        }

        if ($json['view'] == 'si') {
            // list of batch contents
            if (!isset($json['uuid'])) return 'false';
            $rec = Doctrine::getTable('Batch')->findOneBy('batch_uuid', $json['uuid']);
            if (!$rec || !$rec->user_may_read($this_user)) {
                return 'false';
            }

            // enclose in try/catch in case query fails
            try {
                $data = $this->airbatch->fetch_content($rec, $json['params'], $this_user);
            }
            catch (Doctrine_Exception $e) {
                return 'false';
            }
        }
        else {
            // list of batches --- try/catch in case query fails
            try {
                $data = $this->airbatch->fetch($json['params'], null, $this_user);
            }
            catch (Doctrine_Exception $e) {
                return 'false';
            }
        }

        if (!$data) {
            return 'false';
        }
        return json_encode($data);
    }


    /**
     * Returns an inline single-bin object, when the bin view being displayed is
     * 'si' (single), and 'uuid' is set.
     *
     * @return string 'false' or the json-encoded single bin
     */
    public function get_bin_base() {
        $ck = isset($_COOKIE[AIR2_BIN_STATE_CK]) ? $_COOKIE[AIR2_BIN_STATE_CK] : null;
        if (!$ck) return 'false';
        $json = json_decode($ck, true);

        // sanity check!
        if (!$json || !isset($json['view']) || $json['view'] != 'si' || !isset($json['uuid'])) {
            return 'false';
        }

        $data = $this->airbatch->fetch(array(), $json['uuid']);

        if (!$data) {
            return 'false';
        }
        return json_encode($data);
    }


    /**
     * Delete the current bin cookie
     */
    public function delete_bin_cookie() {
        setcookie(AIR2_BIN_STATE_CK, null, time()-3600, '/');
    }


}
